/*
 * Copyright (c) 2014 pkzd/1.0, 颜文泽(robin)
 * All rights reserved.
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *     1 Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     2 Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include	<fs.h>
#include	<map.h>
#include	<conf.h>
#include	<mount.h>

uint rootdev;
struct inode *rootp;
struct mount mount[NMOUNT];

int
iinit(void)
{
	rootdev = ide_slv ? 1 : 0;
	memset(mount, 0, sizeof(mount));
	if(bbread(NRSVSCT, &mount[0].m_spb))
		return -1;
	mount[0].m_dev = rootdev;
	mount[0].m_spb.s_flock = 0;
	mount[0].m_spb.s_ilock = 0;
	mount[0].m_spb.s_ronly = 0;
	if(rootp = iget(rootdev, ROOTINO)){
		rootp->i_flag &= ~ILOCK;
		rootp->i_count++;
		up->p_rdir = up->p_cdir = rootp;
		mfree(swapmap, mount[0].m_spb.s_ssize / 4, (NRSVSCT + SPBSIZ) * 4);	
		return 0;
	}
	return -1;
}

void
update(dev_t dev)
{
	uint i;

	for(i = 0; i < NMOUNT; i++){
		if(mount[i].m_dev == dev)
			if(!bbwrite(NRSVSCT, &mount[i].m_spb))
				mount[i].m_spb.s_fmod = 0;
	}
}

ino_t
ialloc(dev_t dev)
{
	struct buf *bp;
	uint i, j, s, k;
	struct dinode *ip;
	struct filsys *sp;

	sp = &mount[MINOR(dev)].m_spb;
loop:
	while(sp->s_ilock)
		sleep(&sp->s_ilock, PBIO);
	if(sp->s_ninode)
		return sp->s_inode[--sp->s_ninode];
	sp->s_ilock++;
	for(s = i = sp->s_ssize + NRSVSCT + SPBSIZ, j = i + sp->s_isize; i < j; i++){
		bp = bread(dev, i);
		if(bp->b_flags & B_ERROR){
			brelse(bp);
			goto out;
		}
		for(k = 0; k < NIOPB; k++){
			ip = (struct dinode *)bp->b_addr + k;
			if(!ip->i_mode)
				sp->s_inode[sp->s_ninode++] = ((i - s) * NIOPB) + k + 1;
			if(sp->s_ninode == NIFREE){
				brelse(bp);
				goto out;
			}
		}
		brelse(bp);
	}
out:
	sp->s_ilock--;
	if(sp->s_ninode)
		sp->s_fmod = 1;
	wakeup(&sp->s_ilock);
	goto loop;
}

void
ifree(dev_t dev, ino_t ino)
{
	struct filsys *sp;

	sp = &mount[MINOR(dev)].m_spb;
	if(sp->s_ilock)
		return;
	if(sp->s_ninode >= NIFREE)
		return;
	sp->s_fmod = 1;
	sp->s_inode[sp->s_ninode++] = ino;
}

uint
balloc(dev_t dev)
{
	uint bno;
	struct buf *bp;

	if(bno = alloc(dev)){
		bp = bread(dev, bno);	
		if(bp->b_flags & B_ERROR){
			brelse(bp);	
			return 0;
		}
		clrbuf(bp);
		bwrite(bp);
		if(bp->b_flags & B_ERROR)
			return 0;
	}
	return bno;
}

uint
alloc(dev_t dev)
{
	struct buf *bp;
	uint i, j, k, *p;
	struct filsys *sp;

	sp = &mount[MINOR(dev)].m_spb;	
loop:
	while(sp->s_flock)
		sleep(&sp->s_flock, PBIO);
	if(sp->s_nfree)
		return sp->s_free[--sp->s_nfree];
	sp->s_flock++;
	for(i = sp->s_ssize + sp->s_isize + NRSVSCT + SPBSIZ, j = sp->s_fsize; i < j; i += BGRPSIZ){
		bp = bread(dev, i);
		if(bp->b_flags & B_ERROR){
			brelse(bp);
			goto out;
		}
		p = bp->b_addr;
		for(k = 0; k < BGRPSIZ - 1; k++){
			if(!p[k * 2 + 1]){
				p[k * 2 + 1] = 1;
				sp->s_free[sp->s_nfree++] = p[k * 2];
				if(sp->s_nfree == NBFREE){
					bwrite(bp);
					goto out;
				}
			}
		}
		bwrite(bp);
	}
out:
	sp->s_flock--;
	if(sp->s_nfree)
		sp->s_fmod = 1;
        wakeup(&sp->s_flock);
        goto loop;
}

void
free(dev_t dev, uint bn)
{
	uint n, k, *p;
	struct buf *bp;
	struct filsys *sp;

	sp = &mount[MINOR(dev)].m_spb;
	while(sp->s_flock)
		sleep(&sp->s_flock, PBIO);
	if(sp->s_nfree >= NBFREE){
		k = NRSVSCT + SPBSIZ + sp->s_ssize + sp->s_isize;
		n = ((bn - k) / BGRPSIZ) * BGRPSIZ + k;
		bp = bread(dev, n);
		if(bp->b_flags & B_ERROR){
			brelse(bp);
			return;
		}	
		p = bp->b_addr;
		for(k = 0; k < BGRPSIZ - 1; k++){
			if(p[k * 2] == bn){
				p[k * 2 + 1] = 0;
				break;	
			}
		}
		bwrite(bp);
		return;
	}
	sp->s_fmod = 1;
	sp->s_free[sp->s_nfree++] = bn;
}
